<script setup>
import { ref } from 'vue'
import imageCompression from 'browser-image-compression'
import JSZip from 'jszip'

const files = ref([])
const activeTab = ref('resize') // 'resize', 'compress', 'convert'
const processing = ref(false)
const progress = ref(0)
const isDragging = ref(false)
const processedFiles = ref([]) // 存储处理后的文件

// 分辨率调整相关
const resolutionMode = ref('percentage') // 'percentage', 'custom'
const resolutionPercentage = ref(50)
const customWidth = ref(null)
const customHeight = ref(null)
const resizedZip = ref(null)

// 压缩相关
const quality = ref(0.8)
const compressedZip = ref(null)

// 格式转换相关
const targetFormat = ref('png') // 'png', 'jpeg', 'webp'
const convertQuality = ref(0.92) // 转换质量
const convertedZip = ref(null)

const handleFileSelect = (event) => {
  const selectedFiles = Array.from(event.target.files)
  addFiles(selectedFiles)
}

const handleDrop = (event) => {
  event.preventDefault()
  isDragging.value = false

  const droppedFiles = Array.from(event.dataTransfer.files).filter(file =>
    file.type.startsWith('image/')
  )

  if (droppedFiles.length > 0) {
    addFiles(droppedFiles)
  }
}

const handleDragOver = (event) => {
  event.preventDefault()
  isDragging.value = true
}

const handleDragLeave = (event) => {
  event.preventDefault()
  isDragging.value = false
}

const addFiles = (newFiles) => {
  const fileItems = newFiles.map(file => ({
    file,
    name: file.name,
    size: file.size,
    processed: false,
    processedSize: 0,
    preview: URL.createObjectURL(file),
    processedPreview: null
  }))

  files.value.push(...fileItems)
  resetResults()
}

const removeFile = (index) => {
  URL.revokeObjectURL(files.value[index].preview)
  if (files.value[index].processedPreview) {
    URL.revokeObjectURL(files.value[index].processedPreview)
  }
  files.value.splice(index, 1)
}

const clearAll = () => {
  files.value.forEach(item => {
    URL.revokeObjectURL(item.preview)
    if (item.processedPreview) {
      URL.revokeObjectURL(item.processedPreview)
    }
  })
  files.value = []
  resetResults()
}

const resetResults = () => {
  resizedZip.value = null
  compressedZip.value = null
  convertedZip.value = null
  processedFiles.value = []
  files.value.forEach(item => {
    item.processed = false
    item.processedSize = 0
    if (item.processedPreview) {
      URL.revokeObjectURL(item.processedPreview)
      item.processedPreview = null
    }
  })
}

const resizeImages = async () => {
  if (files.value.length === 0) return

  processing.value = true
  progress.value = 0
  resizedZip.value = null
  processedFiles.value = []

  const totalFiles = files.value.length

  for (let i = 0; i < totalFiles; i++) {
    const item = files.value[i]

    try {
      let maxWidthOrHeight = 4096

      if (resolutionMode.value === 'percentage') {
        const img = await createImageBitmap(item.file)
        const scale = resolutionPercentage.value / 100
        maxWidthOrHeight = Math.max(img.width, img.height) * scale
      } else if (resolutionMode.value === 'custom') {
        if (!customWidth.value || !customHeight.value) {
          alert('请填写自定义宽度和高度')
          processing.value = false
          return
        } else {
          maxWidthOrHeight = Math.max(customWidth.value, customHeight.value)
        }
      }

      console.log(`[${i + 1}/${totalFiles}] 开始调整: ${item.name}`)
      
      // 立即更新进度显示，让用户知道正在处理
      progress.value = Math.round((i / totalFiles) * 100)

      const options = {
        maxSizeMB: 100,
        maxWidthOrHeight: maxWidthOrHeight,
        useWebWorker: true,
        quality: 1.0,
        initialQuality: 1.0
      }

      const resizedFile = await imageCompression(item.file, options)
      processedFiles.value.push({ name: item.name, file: resizedFile })

      // 创建处理后的预览
      item.processedPreview = URL.createObjectURL(resizedFile)
      item.processed = true
      item.processedSize = resizedFile.size

      // 更新进度
      progress.value = Math.round(((i + 1) / totalFiles) * 100)

      console.log(`[${i + 1}/${totalFiles}] 完成: ${item.name} (${formatSize(item.size)} → ${formatSize(item.processedSize)})`)

      // 给浏览器时间更新UI，减少卡顿
      await new Promise(resolve => setTimeout(resolve, 50))

    } catch (error) {
      console.error(`[${i + 1}/${totalFiles}] 调整失败:`, item.name, error)
      // 失败时也更新进度
      progress.value = Math.round(((i + 1) / totalFiles) * 100)
    }
  }

  // 准备ZIP（如果多于一张图片）
  if (processedFiles.value.length > 1) {
    console.log('生成ZIP文件...')
    const zip = new JSZip()
    processedFiles.value.forEach(item => {
      zip.file(item.name, item.file)
    })
    resizedZip.value = await zip.generateAsync({ type: 'blob' })
  } else if (processedFiles.value.length === 1) {
    resizedZip.value = 'single'
  }

  console.log('✅ 尺寸调整完成!')
  processing.value = false
}

const compressImages = async () => {
  if (files.value.length === 0) return

  processing.value = true
  progress.value = 0
  compressedZip.value = null
  processedFiles.value = []

  const totalFiles = files.value.length
  
  // 逐个处理文件，避免浏览器卡顿
  for (let i = 0; i < totalFiles; i++) {
    const item = files.value[i]
    
    try {
      // 根据质量调整压缩参数
      let maxSizeMB = 10
      let maxWidthOrHeight = 8192
      
      if (quality.value <= 0.3) {
        // 低质量时减少尺寸限制以加快处理
        maxSizeMB = 3
        maxWidthOrHeight = 2048
      } else if (quality.value <= 0.5) {
        maxSizeMB = 5
        maxWidthOrHeight = 4096
      }
      
      const options = {
        maxSizeMB,
        maxWidthOrHeight,
        useWebWorker: true,
        quality: quality.value,
        initialQuality: Math.min(quality.value + 0.1, 1.0),
        alwaysKeepResolution: false,
        fileType: item.file.type
      }

      console.log(`[${i + 1}/${totalFiles}] 开始压缩: ${item.name}`)
      
      // 立即更新进度显示
      progress.value = Math.round((i / totalFiles) * 100)
      
      const compressedFile = await imageCompression(item.file, options)
      
      // 如果压缩效果不明显且质量设置较低，尝试更激进的压缩
      if (compressedFile.size > item.file.size * 0.8 && quality.value < 0.5) {
        console.log(`[${i + 1}/${totalFiles}] 重新压缩: ${item.name}`)
        const aggressiveOptions = {
          ...options,
          maxSizeMB: Math.max(1, maxSizeMB * 0.5),
          maxWidthOrHeight: Math.max(1024, maxWidthOrHeight * 0.7),
          quality: Math.max(0.1, quality.value * 0.8)
        }
        const recompressed = await imageCompression(item.file, aggressiveOptions)
        processedFiles.value.push({ name: item.name, file: recompressed })
        item.processedPreview = URL.createObjectURL(recompressed)
        item.processedSize = recompressed.size
      } else {
        processedFiles.value.push({ name: item.name, file: compressedFile })
        item.processedPreview = URL.createObjectURL(compressedFile)
        item.processedSize = compressedFile.size
      }
      
      item.processed = true
      progress.value = Math.round(((i + 1) / totalFiles) * 100)
      
      console.log(`[${i + 1}/${totalFiles}] 完成: ${item.name} (${formatSize(item.size)} → ${formatSize(item.processedSize)})`)
      
      // 给浏览器时间更新UI
      await new Promise(resolve => setTimeout(resolve, 50))
      
    } catch (error) {
      console.error('压缩失败:', item.name, error)
      // 压缩失败时使用原文件
      processedFiles.value.push({ name: item.name, file: item.file })
      item.processedPreview = item.preview
      item.processedSize = item.size
      item.processed = true
      
      progress.value = Math.round(((i + 1) / totalFiles) * 100)
    }
  }

  // 准备ZIP（如果多于一张图片）
  if (processedFiles.value.length > 1) {
    console.log('生成ZIP文件...')
    const zip = new JSZip()
    processedFiles.value.forEach(item => {
      zip.file(item.name, item.file)
    })
    compressedZip.value = await zip.generateAsync({ 
      type: 'blob',
      compression: 'DEFLATE',
      compressionOptions: { level: 6 }
    })
  } else if (processedFiles.value.length === 1) {
    compressedZip.value = 'single'
  }

  console.log('✅ 压缩完成!')
  processing.value = false
}

const downloadZip = (blob, prefix) => {
  if (!blob) return

  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url
  a.download = `${prefix}_${Date.now()}.zip`
  a.click()
  URL.revokeObjectURL(url)
}

const downloadSingle = () => {
  if (processedFiles.value.length !== 1) return

  const file = processedFiles.value[0]
  const url = URL.createObjectURL(file.file)
  const a = document.createElement('a')
  a.href = url
  a.download = file.name
  a.click()
  URL.revokeObjectURL(url)
}

const formatSize = (bytes) => {
  if (bytes === 0) return '0 B'
  const k = 1024
  const sizes = ['B', 'KB', 'MB', 'GB']
  const i = Math.floor(Math.log(bytes) / Math.log(k))
  return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i]
}

const getCompressionRatio = (item) => {
  if (!item.processed) return '-'
  const ratio = ((1 - item.processedSize / item.size) * 100).toFixed(1)
  return ratio + '%'
}

// 格式转换功能
const convertImages = async () => {
  if (files.value.length === 0) return

  processing.value = true
  progress.value = 0
  convertedZip.value = null
  processedFiles.value = []

  const totalFiles = files.value.length

  for (let i = 0; i < totalFiles; i++) {
    const item = files.value[i]

    try {
      console.log(`[${i + 1}/${totalFiles}] 开始转换: ${item.name}`)
      
      // 立即更新进度显示
      progress.value = Math.round((i / totalFiles) * 100)

      // 创建 canvas 进行格式转换
      const img = await createImageBitmap(item.file)
      const canvas = document.createElement('canvas')
      canvas.width = img.width
      canvas.height = img.height
      const ctx = canvas.getContext('2d')
      ctx.drawImage(img, 0, 0)

      // 转换为目标格式
      const blob = await new Promise((resolve) => {
        canvas.toBlob(
          (blob) => resolve(blob),
          `image/${targetFormat.value}`,
          convertQuality.value
        )
      })

      // 生成新文件名
      const originalName = item.name.substring(0, item.name.lastIndexOf('.'))
      const newFileName = `${originalName}.${targetFormat.value}`
      const convertedFile = new File([blob], newFileName, {
        type: `image/${targetFormat.value}`
      })

      processedFiles.value.push({ name: newFileName, file: convertedFile })

      // 创建处理后的预览
      item.processedPreview = URL.createObjectURL(convertedFile)
      item.processed = true
      item.processedSize = convertedFile.size

      progress.value = Math.round(((i + 1) / totalFiles) * 100)

      console.log(`[${i + 1}/${totalFiles}] 完成: ${item.name} (${formatSize(item.size)} → ${formatSize(item.processedSize)})`)

      // 给浏览器时间更新UI
      await new Promise(resolve => setTimeout(resolve, 50))

    } catch (error) {
      console.error(`[${i + 1}/${totalFiles}] 转换失败:`, item.name, error)
      progress.value = Math.round(((i + 1) / totalFiles) * 100)
    }
  }

  // 准备ZIP（如果多于一张图片）
  if (processedFiles.value.length > 1) {
    console.log('生成ZIP文件...')
    const zip = new JSZip()
    processedFiles.value.forEach(item => {
      zip.file(item.name, item.file)
    })
    convertedZip.value = await zip.generateAsync({ type: 'blob' })
  } else if (processedFiles.value.length === 1) {
    convertedZip.value = 'single'
  }

  console.log('✅ 格式转换完成!')
  processing.value = false
}
</script>

<template>
  <div class="container">
    <div class="header">
      <h1>🖼️ 图片处理工具</h1>
      <p>支持批量处理，纯前端运行</p>
    </div>

    <!-- 拖拽上传区域 -->
    <div class="drop-zone" :class="{ dragging: isDragging }" @drop="handleDrop" @dragover="handleDragOver"
      @dragleave="handleDragLeave">
      <div class="drop-zone-content">
        <div class="drop-icon">📷</div>
        <div class="drop-text">
          <strong>拖拽图片到这里</strong>
          <span>或</span>
        </div>
        <input type="file" id="fileInput" multiple accept="image/*" @change="handleFileSelect" :disabled="processing" />
        <label for="fileInput" class="file-input-label">
          📁 点击选择文件
        </label>
        <div class="drop-hint">支持多图批量上传</div>
      </div>
    </div>

    <!-- Tab 切换 -->
    <div v-if="files.length > 0" class="tabs">
      <button class="tab-btn" :class="{ active: activeTab === 'resize' }" @click="activeTab = 'resize'; resetResults()"
        :disabled="processing">
        📐 尺寸调整
      </button>
      <button class="tab-btn" :class="{ active: activeTab === 'compress' }"
        @click="activeTab = 'compress'; resetResults()" :disabled="processing">
        🗜️ 图片压缩
      </button>
      <button class="tab-btn" :class="{ active: activeTab === 'convert' }"
        @click="activeTab = 'convert'; resetResults()" :disabled="processing">
        🔄 格式转换
      </button>
    </div>

    <!-- 文件列表 -->
    <div v-if="files.length > 0" class="file-list">
      <div class="list-header">
        <span>已选择 {{ files.length }} 个文件</span>
        <button v-if="!processing" @click="clearAll" class="clear-btn">
          清空列表
        </button>
      </div>

      <div class="files">
        <div v-for="(item, index) in files" :key="index" class="file-item">
          <img :src="item.preview" class="preview" />
          <div class="file-info">
            <div class="file-name">{{ item.name }}</div>
            <div class="file-size">
              <span>原始: {{ formatSize(item.size) }}</span>
              <span v-if="item.processed" class="processed">
                → 处理后: {{ formatSize(item.processedSize) }}
                <span class="ratio">(减少 {{ getCompressionRatio(item) }})</span>
              </span>
            </div>
          </div>
          <button v-if="!processing" @click="removeFile(index)" class="remove-btn">
            ✕
          </button>
        </div>
      </div>
    </div>

    <!-- 尺寸调整工具 -->
    <div v-if="files.length > 0 && activeTab === 'resize'" class="tool-panel">
      <h3>📐 尺寸调整设置</h3>

      <div class="resolution-options">
        <label class="radio-label">
          <input type="radio" value="percentage" v-model="resolutionMode" :disabled="processing" />
          按比例缩放
        </label>
        <label class="radio-label">
          <input type="radio" value="custom" v-model="resolutionMode" :disabled="processing" />
          自定义尺寸
        </label>
      </div>

      <div v-if="resolutionMode === 'percentage'" class="option-input">
        <label for="percentage">缩放比例:</label>
        <div class="input-with-unit">
          <input id="percentage" type="number" v-model.number="resolutionPercentage" min="1" max="200"
            :disabled="processing" />
          <span class="unit">%</span>
        </div>
        <p class="hint">💡 {{ resolutionPercentage }}% = 长宽都缩放到原来的 {{ resolutionPercentage }}%</p>
      </div>

      <div v-if="resolutionMode === 'custom'" class="option-input">
        <div class="custom-size">
          <div class="size-input">
            <label for="width">最大宽度:</label>
            <div class="input-with-unit">
              <input id="width" type="number" v-model.number="customWidth" min="1" :disabled="processing" />
              <span class="unit">px</span>
            </div>
          </div>
          <div class="size-input">
            <label for="height">最大高度:</label>
            <div class="input-with-unit">
              <input id="height" type="number" v-model.number="customHeight" min="1" :disabled="processing" />
              <span class="unit">px</span>
            </div>
          </div>
        </div>
        <p class="hint">💡 保持厚图比例，以最大边为准</p>
      </div>

      <button v-if="!processing && !resizedZip" @click="resizeImages" class="action-btn">
        🚀 开始调整尺寸
      </button>
    </div>

    <!-- 图片压缩工具 -->
    <div v-if="files.length > 0 && activeTab === 'compress'" class="tool-panel">
      <h3>🗜️ 图片压缩设置</h3>

      <div class="quality-control">
        <label for="quality">压缩质量: {{ (quality * 100).toFixed(0) }}%</label>
        
        <!-- 快速预设 -->
        <div class="quality-presets">
          <button 
            class="preset-btn" 
            :class="{ active: quality === 0.9 }"
            @click="quality = 0.9"
            :disabled="processing"
          >
            高质量 (90%)
          </button>
          <button 
            class="preset-btn" 
            :class="{ active: quality === 0.7 }"
            @click="quality = 0.7"
            :disabled="processing"
          >
            平衡 (70%)
          </button>
          <button 
            class="preset-btn" 
            :class="{ active: quality === 0.5 }"
            @click="quality = 0.5"
            :disabled="processing"
          >
            小体积 (50%)
          </button>
          <button 
            class="preset-btn" 
            :class="{ active: quality === 0.2 }"
            @click="quality = 0.2"
            :disabled="processing"
          >
            极限压缩 (20%)
          </button>
        </div>
        
        <input id="quality" type="range" v-model.number="quality" min="0.1" max="1" step="0.05"
          :disabled="processing" />
        <p class="hint">
          💡 {{ quality <= 0.3 ? '极低质量会显著降低图片清晰度，但体积最小' : 
                quality <= 0.6 ? '中等质量平衡了体积和清晰度' : 
                '高质量保持较好清晰度，体积较大' }}
        </p>
      </div>

      <button v-if="!processing && !compressedZip" @click="compressImages" class="action-btn">
        🚀 开始压缩
      </button>
    </div>

    <!-- 格式转换工具 -->
    <div v-if="files.length > 0 && activeTab === 'convert'" class="tool-panel">
      <h3>🔄 格式转换设置</h3>

      <div class="format-options">
        <label class="option-label">目标格式:</label>
        <div class="format-buttons">
          <button 
            class="format-btn" 
            :class="{ active: targetFormat === 'png' }"
            @click="targetFormat = 'png'"
            :disabled="processing"
          >
            <span class="format-icon">🖼️</span>
            <span class="format-name">PNG</span>
            <span class="format-desc">无损质量</span>
          </button>
          <button 
            class="format-btn" 
            :class="{ active: targetFormat === 'jpeg' }"
            @click="targetFormat = 'jpeg'"
            :disabled="processing"
          >
            <span class="format-icon">📷</span>
            <span class="format-name">JPEG</span>
            <span class="format-desc">小体积</span>
          </button>
          <button 
            class="format-btn" 
            :class="{ active: targetFormat === 'webp' }"
            @click="targetFormat = 'webp'"
            :disabled="processing"
          >
            <span class="format-icon">✨</span>
            <span class="format-name">WebP</span>
            <span class="format-desc">现代格式</span>
          </button>
        </div>
      </div>

      <div class="quality-control">
        <label for="convertQuality">转换质量: {{ (convertQuality * 100).toFixed(0) }}%</label>
        <input id="convertQuality" type="range" v-model.number="convertQuality" min="0.1" max="1" step="0.05"
          :disabled="processing" />
        <p class="hint">💡 {{ targetFormat.toUpperCase() }} 格式的输出质量，质量越高文件越大</p>
      </div>

      <button v-if="!processing && !convertedZip" @click="convertImages" class="action-btn">
        🚀 开始转换
      </button>
    </div>

    <!-- 进度条 -->
    <div v-if="processing" class="progress">
      <div class="progress-bar">
        <div class="progress-fill" :style="{ width: progress + '%' }"></div>
      </div>
      <div class="progress-text">处理中... {{ progress }}%</div>
    </div>

    <!-- 下载区域 -->
    <div v-if="resizedZip || compressedZip || convertedZip" class="result-section">
      <div class="success-message">
        ✅ 处理完成!共处理 {{ files.length }} 个文件
      </div>

      <!-- 处理后的图片预览 -->
      <div class="result-preview">
        <h3>🖼️ 处理后的图片预览</h3>
        <div class="preview-grid">
          <div v-for="(item, index) in files" :key="index" class="preview-item">
            <div class="preview-compare">
              <div class="preview-box">
                <img :src="item.preview" class="preview-img" />
                <div class="preview-label">原图</div>
                <div class="preview-size">{{ formatSize(item.size) }}</div>
              </div>
              <div class="preview-arrow">→</div>
              <div class="preview-box">
                <img :src="item.processedPreview" class="preview-img" />
                <div class="preview-label processed">处理后</div>
                <div class="preview-size processed">{{ formatSize(item.processedSize) }}</div>
              </div>
            </div>
            <div class="preview-name">{{ item.name }}</div>
            <div class="preview-ratio">体积减少: {{ getCompressionRatio(item) }}</div>
          </div>
        </div>
      </div>

      <!-- 下载按钮 -->
      <div class="download-buttons">
        <button v-if="resizedZip === 'single'" @click="downloadSingle" class="download-btn primary">
          ⬇️ 下载调整后的图片
        </button>
        <button v-if="resizedZip && resizedZip !== 'single'" @click="downloadZip(resizedZip, 'resized_images')"
          class="download-btn primary">
          ⬇️ 下载调整后的图片 (ZIP)
        </button>
        <button v-if="compressedZip === 'single'" @click="downloadSingle" class="download-btn primary">
          ⬇️ 下载压缩后的图片
        </button>
        <button v-if="compressedZip && compressedZip !== 'single'"
          @click="downloadZip(compressedZip, 'compressed_images')" class="download-btn primary">
          ⬇️ 下载压缩后的图片 (ZIP)
        </button>
        <button v-if="convertedZip === 'single'" @click="downloadSingle" class="download-btn primary">
          ⬇️ 下载转换后的图片
        </button>
        <button v-if="convertedZip && convertedZip !== 'single'"
          @click="downloadZip(convertedZip, 'converted_images')" class="download-btn primary">
          ⬇️ 下载转换后的图片 (ZIP)
        </button>
      </div>
    </div>
  </div>
</template>

<style scoped>
.container {
  max-width: 1000px;
  margin: 0 auto;
  padding: 2rem 1rem;
}

.header {
  text-align: center;
  margin-bottom: 2rem;
}

.header h1 {
  color: #42b883;
  margin-bottom: 0.5rem;
  font-size: 2rem;
}

.header p {
  color: #666;
  font-size: 0.9rem;
}

/* 拖拽上传区域 */
.drop-zone {
  border: 3px dashed #d0d0d0;
  border-radius: 12px;
  padding: 3rem 2rem;
  margin-bottom: 2rem;
  background: #fafafa;
  transition: all 0.3s;
  cursor: pointer;
}

.drop-zone.dragging {
  border-color: #42b883;
  background: #f0fdf4;
  transform: scale(1.02);
}

.drop-zone-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
}

.drop-icon {
  font-size: 4rem;
}

.drop-text {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  color: #666;
}

.drop-text strong {
  font-size: 1.2rem;
  color: #333;
}

.drop-hint {
  font-size: 0.85rem;
  color: #999;
}

.drop-zone input[type="file"] {
  display: none;
}

.file-input-label {
  padding: 0.8rem 2rem;
  background: #42b883;
  color: white;
  border-radius: 8px;
  cursor: pointer;
  font-weight: 500;
  transition: background 0.3s;
  display: inline-block;
}

.file-input-label:hover {
  background: #35a372;
}

/* Tab 切换 */
.tabs {
  display: flex;
  gap: 1rem;
  margin-bottom: 2rem;
  border-bottom: 2px solid #e0e0e0;
}

.tab-btn {
  flex: 1;
  padding: 1rem;
  background: none;
  border: none;
  border-bottom: 3px solid transparent;
  font-size: 1.1rem;
  font-weight: 600;
  color: #666;
  cursor: pointer;
  transition: all 0.3s;
  margin-bottom: -2px;
}

.tab-btn:hover:not(:disabled) {
  color: #42b883;
}

.tab-btn.active {
  color: #42b883;
  border-bottom-color: #42b883;
}

.tab-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* 文件列表 */
.file-list {
  margin-bottom: 2rem;
}

.list-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 1rem;
  background: #f0f0f0;
  border-radius: 8px 8px 0 0;
  font-weight: 500;
}

.clear-btn {
  padding: 0.5rem 1rem;
  background: #ff6b6b;
  color: white;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  font-size: 0.9rem;
  transition: background 0.3s;
}

.clear-btn:hover {
  background: #ee5a52;
}

.files {
  border: 1px solid #e0e0e0;
  border-top: none;
  border-radius: 0 0 8px 8px;
  overflow: hidden;
  max-height: 400px;
  overflow-y: auto;
}

.file-item {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1rem;
  border-bottom: 1px solid #e0e0e0;
  transition: background 0.2s;
  background: white;
}

.file-item:last-child {
  border-bottom: none;
}

.file-item:hover {
  background: #fafafa;
}

.preview {
  width: 60px;
  height: 60px;
  object-fit: cover;
  border-radius: 6px;
  border: 1px solid #e0e0e0;
}

.file-info {
  flex: 1;
  min-width: 0;
}

.file-name {
  font-weight: 500;
  margin-bottom: 0.3rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.file-size {
  font-size: 0.85rem;
  color: #666;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}

.processed {
  color: #42b883;
  font-weight: 500;
}

.ratio {
  color: #35a372;
}

.remove-btn {
  padding: 0.5rem 1rem;
  background: #ff4444;
  color: white;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  font-size: 1rem;
  transition: background 0.3s;
  flex-shrink: 0;
}

.remove-btn:hover {
  background: #cc0000;
}

/* 工具面板 */
.tool-panel {
  padding: 2rem;
  background: white;
  border-radius: 12px;
  border: 2px solid #e0e0e0;
  margin-bottom: 2rem;
}

.tool-panel h3 {
  margin: 0 0 1.5rem 0;
  color: #333;
  font-size: 1.3rem;
}

.resolution-options {
  display: flex;
  gap: 2rem;
  margin-bottom: 1.5rem;
  flex-wrap: wrap;
}

.radio-label {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  cursor: pointer;
  font-size: 1rem;
}

.radio-label input[type="radio"] {
  cursor: pointer;
  width: 18px;
  height: 18px;
}

.option-input {
  padding: 1.5rem;
  background: #f9fafb;
  border-radius: 8px;
  margin-bottom: 1.5rem;
}

.option-input>label {
  display: block;
  font-weight: 500;
  color: #333;
  margin-bottom: 0.8rem;
}

.input-with-unit {
  display: flex;
  align-items: center;
  gap: 0.8rem;
}

.input-with-unit input {
  flex: 1;
  padding: 0.8rem;
  border: 2px solid #d0d0d0;
  border-radius: 6px;
  font-size: 1rem;
  transition: border-color 0.3s;
}

.input-with-unit input:focus {
  outline: none;
  border-color: #42b883;
}

.input-with-unit .unit {
  font-weight: 600;
  color: #666;
  min-width: 40px;
  font-size: 1.1rem;
}

.custom-size {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.5rem;
  margin-bottom: 1rem;
}

.size-input label {
  display: block;
  font-size: 0.9rem;
  color: #666;
  margin-bottom: 0.5rem;
}

.hint {
  font-size: 0.9rem;
  color: #42b883;
  margin: 0.8rem 0 0 0;
  padding: 0.8rem;
  background: #f0fdf4;
  border-radius: 6px;
  border-left: 3px solid #42b883;
}

.quality-control {
  margin-bottom: 1.5rem;
}

.quality-control label {
  display: block;
  font-weight: 500;
  color: #333;
  margin-bottom: 1rem;
  font-size: 1.1rem;
}

.quality-presets {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0.5rem;
  margin-bottom: 1rem;
}

.preset-btn {
  padding: 0.6rem 0.8rem;
  background: white;
  border: 2px solid #e0e0e0;
  border-radius: 6px;
  cursor: pointer;
  font-size: 0.85rem;
  font-weight: 500;
  transition: all 0.3s;
  color: #666;
}

.preset-btn:hover:not(:disabled) {
  border-color: #42b883;
  color: #42b883;
}

.preset-btn.active {
  background: #42b883;
  border-color: #42b883;
  color: white;
}

.preset-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.quality-control input[type="range"] {
  width: 100%;
  height: 8px;
  cursor: pointer;
  margin-bottom: 0.5rem;
}

/* 格式转换样式 */
.format-options {
  margin-bottom: 1.5rem;
}

.option-label {
  display: block;
  font-weight: 500;
  color: #333;
  margin-bottom: 1rem;
  font-size: 1.1rem;
}

.format-buttons {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
}

.format-btn {
  padding: 1.2rem;
  background: white;
  border: 2px solid #e0e0e0;
  border-radius: 10px;
  cursor: pointer;
  transition: all 0.3s;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
}

.format-btn:hover:not(:disabled) {
  border-color: #42b883;
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(66, 184, 131, 0.2);
}

.format-btn.active {
  border-color: #42b883;
  background: #f0fdf4;
}

.format-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.format-icon {
  font-size: 2rem;
}

.format-name {
  font-size: 1.1rem;
  font-weight: 600;
  color: #333;
}

.format-desc {
  font-size: 0.85rem;
  color: #666;
}

/* 按钮 */
.action-btn {
  width: 100%;
  padding: 1.2rem;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  border: none;
  border-radius: 8px;
  font-size: 1.1rem;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.3s;
}

.action-btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
}

.action-btn:active {
  transform: translateY(0);
}

/* 进度条 */
.progress {
  margin-bottom: 2rem;
}

.progress-bar {
  width: 100%;
  height: 35px;
  background: #e0e0e0;
  border-radius: 18px;
  overflow: hidden;
  margin-bottom: 0.8rem;
}

.progress-fill {
  height: 100%;
  background: linear-gradient(90deg, #42b883, #35a372);
  transition: width 0.3s;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding-right: 1rem;
  color: white;
  font-weight: 600;
}

.progress-text {
  text-align: center;
  font-weight: 600;
  color: #42b883;
  font-size: 1.1rem;
}

/* 下载区域 */
.result-section {
  padding: 2rem;
  background: #f0fdf4;
  border-radius: 12px;
  border: 2px solid #42b883;
  margin-bottom: 2rem;
}

.success-message {
  font-size: 1.3rem;
  font-weight: 600;
  color: #42b883;
  text-align: center;
  margin-bottom: 2rem;
}

.result-preview h3 {
  color: #333;
  margin-bottom: 1.5rem;
  font-size: 1.2rem;
  text-align: center;
}

.preview-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 2rem;
  margin-bottom: 2rem;
}

.preview-item {
  background: white;
  border-radius: 12px;
  padding: 1.5rem;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.preview-compare {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  margin-bottom: 1rem;
}

.preview-box {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
}

.preview-img {
  width: 100%;
  height: 150px;
  object-fit: contain;
  border-radius: 8px;
  border: 2px solid #e0e0e0;
  background: #fafafa;
}

.preview-label {
  font-size: 0.85rem;
  font-weight: 500;
  color: #666;
}

.preview-label.processed {
  color: #42b883;
}

.preview-size {
  font-size: 0.9rem;
  color: #999;
  font-weight: 500;
}

.preview-size.processed {
  color: #42b883;
  font-weight: 600;
}

.preview-arrow {
  font-size: 1.5rem;
  color: #42b883;
  font-weight: bold;
}

.preview-name {
  font-size: 0.9rem;
  color: #333;
  text-align: center;
  margin-bottom: 0.5rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.preview-ratio {
  font-size: 0.9rem;
  color: #42b883;
  text-align: center;
  font-weight: 600;
}

.download-buttons {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  width: 100%;
  max-width: 500px;
  margin: 0 auto;
}

.download-btn {
  width: 100%;
  padding: 1.2rem 2rem;
  background: linear-gradient(135deg, #42b883 0%, #35a372 100%);
  color: white;
  border: none;
  border-radius: 8px;
  font-size: 1.1rem;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.3s;
}

.download-btn.primary {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}

.download-btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 20px rgba(66, 184, 131, 0.4);
}

.download-btn.primary:hover {
  box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
}

.download-btn:active {
  transform: translateY(0);
}

/* 响应式 */
@media (max-width: 768px) {
  .custom-size {
    grid-template-columns: 1fr;
  }

  .tabs {
    flex-direction: column;
    gap: 0;
  }

  .tab-btn {
    border-bottom: 1px solid #e0e0e0;
  }

  .resolution-options {
    flex-direction: column;
    gap: 1rem;
  }

  .preview-grid {
    grid-template-columns: 1fr;
  }

  .preview-compare {
    flex-direction: column;
  }

  .preview-arrow {
    transform: rotate(90deg);
  }

  .format-buttons {
    grid-template-columns: 1fr;
  }

  .quality-presets {
    grid-template-columns: repeat(2, 1fr);
    gap: 0.8rem;
  }
}
</style>
